package server.session;

import datastorage.DataStorage;
import org.apache.log4j.Logger;
import server.Communicator;
import server.Connector;
import server.session.handler.RequestHandler;
import server.session.handler.RequestHandlerFactory;
import unit.request.Request;
import unit.response.ContactInfoChangedMessage;
import unit.response.ServerMessage;

import java.io.IOException;
import java.net.SocketException;
import java.net.SocketTimeoutException;

/**
 * @author Yuri Denison
 * @since 06.08.11
 */
public class UserSession {
    public static long DELAY = 100;

    private int session;
    private int userId;
    private final Connector connector;
    private DataStorage dataStorage;
    private Communicator communicator;

    private RequestHandlerFactory handlerFactory;

    private Thread worker;
    private volatile boolean stopped = false;

    private Logger log = Logger.getLogger(UserSession.class);

    public UserSession(int session, final Connector connector, DataStorage dataStorage, Communicator communicator) {
        this.session = session;
        this.connector = connector;
        this.dataStorage = dataStorage;
        this.communicator = communicator;
        this.handlerFactory = RequestHandlerFactory.getInstance();
        userId = -1;

        this.start();

        log.debug("New session was created");
    }

    public void handleRequest(Request request) {
        RequestHandler handler = handlerFactory.getHandler(request.getClass());
        handler.handleRequest(request, this);
    }

    public void start() {
        worker = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!stopped) {
                    synchronized (connector) {
                        try {
                            Request request = (Request) connector.read();
                            if (request != null) {
                                handleRequest(request);
                            }
                            connector.wait(DELAY);
                        } catch (SocketTimeoutException e) {
                            log.error("Socket Timeout Exception");
                        } catch (SocketException e) {
                            log.error("User session disconnected [ID " + userId + "; Session: " + session + "]. Set offline");
                            setOffline();
                            break;
                        } catch (IOException e) {
                            log.error("IO Exception. Set offline");
                            log.error(e);
                            setOffline();
                            break;
                        } catch (InterruptedException e) {
                            setOffline();
                            log.error("Someone interrupted me! :[");
                            break;
                        }
                    }
                }
            }
        });
        worker.start();
    }

    private void setOffline() {
        if (userId == -1) {
            return;
        }
        communicator.removeUserSession(this);
        if (communicator.getSessionNumber(userId) == 0) {
            String[] projective = new String[]{"status"};
            String[] values = new String[]{"offline"};

            dataStorage.changeUserInfo(userId, projective, values);
            communicator.send(
                    dataStorage.getContactList(userId),
                    new ContactInfoChangedMessage(session, userId, projective, values)
            );
        }
    }

    public void setOnline() {
        if (communicator.getSessionNumber(userId) == 0) {
            String[] projective = new String[]{"status"};
            String[] values = new String[]{"online"};

            dataStorage.changeUserInfo(userId, projective, values);
            communicator.send(
                    dataStorage.getContactList(userId),
                    new ContactInfoChangedMessage(session, userId, projective, values)
            );
        }
        communicator.addUserSession(this);
    }

    public void stop() {
        stopped = true;
        worker = null;
    }

    public boolean send(ServerMessage serverMessage) {
        try {
            connector.send(serverMessage);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            log.error("Fail sending server message from server.session.UserSession!");
            return false;
        }
    }

    public int getUserId() {
        return userId;
    }

    public int getSession() {
        return session;
    }

    public DataStorage getDataStorage() {
        return dataStorage;
    }

    public Connector getConnector() {
        return connector;
    }

    public Communicator getCommunicator() {
        return communicator;
    }

    public void setUserId(int userId) {
        if (userId == -1) {
            log.debug("userId changed: " + userId + " (" + this.userId + ")");
            this.userId = userId;
        } else {
            log.error("tried to change userId of active userSession");
        }
    }
}